package com.hongshu.web.service.recommendation;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 实时推荐系统
 * 处理用户行为实时更新、用户画像实时计算、热度实时统计等
 *
 * @author hongshu
 * @date 2024/01/01
 */
@Service
@Slf4j
public class RealTimeRecommendationService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private UserProfileService userProfileService;

    @Autowired
    private SimilarityCalculator similarityCalculator;

    /**
     * 处理用户行为事件
     */
    @EventListener
    public void handleUserBehavior(UserBehaviorEvent event) {
        try {
            log.info("处理用户行为事件: {}", event);

            String userId = event.getUserId();
            String itemId = event.getItemId();
            BehaviorType behaviorType = event.getBehaviorType();

            // 1. 更新用户实时兴趣
            updateUserRealTimeInterest(userId, itemId, behaviorType);

            // 2. 更新物品热度
            updateItemPopularity(itemId, behaviorType);

            // 3. 更新用户相似度
            updateUserSimilarity(userId, itemId, behaviorType);

            // 4. 更新标签热度
            updateTagPopularity(itemId, behaviorType);

            // 5. 更新用户画像
            updateUserProfile(userId, itemId, behaviorType);

            log.info("用户行为事件处理完成");

        } catch (Exception e) {
            log.error("处理用户行为事件异常", e);
        }
    }

    /**
     * 更新用户实时兴趣
     */
    private void updateUserRealTimeInterest(String userId, String itemId, BehaviorType behaviorType) {
        try {
            String key = "user_realtime_interest:" + userId;

            // 获取物品标签
            List<String> itemTags = getItemTags(itemId);

            // 计算兴趣权重
            double weight = getBehaviorWeight(behaviorType);

            // 更新每个标签的兴趣分数
            for (String tag : itemTags) {
                String tagKey = key + ":" + tag;
                Double currentScore = (Double) redisTemplate.opsForValue().get(tagKey);
                double newScore = currentScore != null ? currentScore + weight : weight;

                // 设置过期时间（7天）
                redisTemplate.opsForValue().set(tagKey, newScore, 7, TimeUnit.DAYS);
            }

            log.debug("更新用户实时兴趣完成: userId={}, itemId={}, tags={}", userId, itemId, itemTags);

        } catch (Exception e) {
            log.error("更新用户实时兴趣异常", e);
        }
    }

    /**
     * 更新物品热度
     */
    private void updateItemPopularity(String itemId, BehaviorType behaviorType) {
        try {
            String key = "item_popularity:" + itemId;

            // 计算热度权重
            double weight = getBehaviorWeight(behaviorType);

            // 更新热度分数
            Double currentPopularity = (Double) redisTemplate.opsForValue().get(key);
            double newPopularity = currentPopularity != null ? currentPopularity + weight : weight;

            // 设置过期时间（30天）
            redisTemplate.opsForValue().set(key, newPopularity, 30, TimeUnit.DAYS);

            // 更新热门物品排行榜
            updateTrendingRanking(itemId, newPopularity);

            log.debug("更新物品热度完成: itemId={}, popularity={}", itemId, newPopularity);

        } catch (Exception e) {
            log.error("更新物品热度异常", e);
        }
    }

    /**
     * 更新用户相似度
     */
    private void updateUserSimilarity(String userId, String itemId, BehaviorType behaviorType) {
        try {
            // 获取对该物品有行为的其他用户
            List<String> otherUsers = getUsersWhoInteractedWithItem(itemId);

            for (String otherUserId : otherUsers) {
                if (!otherUserId.equals(userId)) {
                    // 计算并更新用户相似度
                    double similarity = similarityCalculator.calculateSimilarity(userId, otherUserId);
                    updateUserSimilarityCache(userId, otherUserId, similarity);
                }
            }

            log.debug("更新用户相似度完成: userId={}, itemId={}", userId, itemId);

        } catch (Exception e) {
            log.error("更新用户相似度异常", e);
        }
    }

    /**
     * 更新标签热度
     */
    private void updateTagPopularity(String itemId, BehaviorType behaviorType) {
        try {
            List<String> itemTags = getItemTags(itemId);
            double weight = getBehaviorWeight(behaviorType);

            for (String tag : itemTags) {
                String key = "tag_popularity:" + tag;
                Double currentPopularity = (Double) redisTemplate.opsForValue().get(key);
                double newPopularity = currentPopularity != null ? currentPopularity + weight : weight;

                // 设置过期时间（30天）
                redisTemplate.opsForValue().set(key, newPopularity, 30, TimeUnit.DAYS);
            }

            log.debug("更新标签热度完成: itemId={}, tags={}", itemId, itemTags);

        } catch (Exception e) {
            log.error("更新标签热度异常", e);
        }
    }

    /**
     * 更新用户画像
     */
    private void updateUserProfile(String userId, String itemId, BehaviorType behaviorType) {
        try {
            // 更新用户行为计数
            String behaviorKey = "user_behavior_count:" + userId;
            Long behaviorCount = redisTemplate.opsForValue().increment(behaviorKey);
            redisTemplate.expire(behaviorKey, 30, TimeUnit.DAYS);

            // 更新用户最近行为
            String recentBehaviorKey = "user_recent_behavior:" + userId;
            redisTemplate.opsForList().leftPush(recentBehaviorKey, itemId + ":" + behaviorType.name());
            redisTemplate.opsForList().trim(recentBehaviorKey, 0, 99); // 保留最近100个行为
            redisTemplate.expire(recentBehaviorKey, 7, TimeUnit.DAYS);

            log.debug("更新用户画像完成: userId={}, behaviorCount={}", userId, behaviorCount);

        } catch (Exception e) {
            log.error("更新用户画像异常", e);
        }
    }

    /**
     * 更新热门物品排行榜
     */
    private void updateTrendingRanking(String itemId, double popularity) {
        try {
            String key = "trending_items";
            redisTemplate.opsForZSet().add(key, itemId, popularity);

            // 只保留前1000个热门物品
            redisTemplate.opsForZSet().removeRange(key, 0, -1001);

            log.debug("更新热门物品排行榜完成: itemId={}, popularity={}", itemId, popularity);

        } catch (Exception e) {
            log.error("更新热门物品排行榜异常", e);
        }
    }

    /**
     * 获取行为权重
     */
    private double getBehaviorWeight(BehaviorType behaviorType) {
        switch (behaviorType) {
            case LIKE:
                return 2.0;
            case COMMENT:
                return 3.0;
            case SHARE:
                return 4.0;
            case COLLECT:
                return 3.5;
            case VIEW:
                return 1.0;
            case CLICK:
                return 1.5;
            default:
                return 1.0;
        }
    }

    /**
     * 获取物品标签
     */
    private List<String> getItemTags(String itemId) {
        String key = "item_tags:" + itemId;
        @SuppressWarnings("unchecked")
        List<String> tags = (List<String>) redisTemplate.opsForValue().get(key);
        return tags != null ? tags : new ArrayList<>();
    }

    /**
     * 获取对物品有行为的其他用户
     */
    private List<String> getUsersWhoInteractedWithItem(String itemId) {
        String key = "item_users:" + itemId;
        @SuppressWarnings("unchecked")
        List<String> users = (List<String>) redisTemplate.opsForValue().get(key);
        return users != null ? users : new ArrayList<>();
    }

    /**
     * 更新用户相似度缓存
     */
    private void updateUserSimilarityCache(String userId1, String userId2, double similarity) {
        String key = "user_similarity:" + userId1 + ":" + userId2;
        redisTemplate.opsForValue().set(key, similarity, 7, TimeUnit.DAYS);

        // 同时更新反向相似度
        String reverseKey = "user_similarity:" + userId2 + ":" + userId1;
        redisTemplate.opsForValue().set(reverseKey, similarity, 7, TimeUnit.DAYS);
    }

    /**
     * 相似度计算器
     */
    @Service
    public static class SimilarityCalculator {

        @Autowired
        private RedisTemplate<String, Object> redisTemplate;

        /**
         * 计算用户相似度（余弦相似度）
         */
        public double calculateSimilarity(String userId1, String userId2) {
            try {
                // 获取两个用户的兴趣向量
                Map<String, Double> user1Interests = getUserInterestVector(userId1);
                Map<String, Double> user2Interests = getUserInterestVector(userId2);

                if (user1Interests.isEmpty() || user2Interests.isEmpty()) {
                    return 0.0;
                }

                // 计算余弦相似度
                double dotProduct = 0.0;
                double norm1 = 0.0;
                double norm2 = 0.0;

                Set<String> allTags = new HashSet<>(user1Interests.keySet());
                allTags.addAll(user2Interests.keySet());

                for (String tag : allTags) {
                    double score1 = user1Interests.getOrDefault(tag, 0.0);
                    double score2 = user2Interests.getOrDefault(tag, 0.0);

                    dotProduct += score1 * score2;
                    norm1 += score1 * score1;
                    norm2 += score2 * score2;
                }

                if (norm1 == 0.0 || norm2 == 0.0) {
                    return 0.0;
                }

                return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));

            } catch (Exception e) {
                log.error("计算用户相似度异常", e);
                return 0.0;
            }
        }

        /**
         * 获取用户兴趣向量
         */
        private Map<String, Double> getUserInterestVector(String userId) {
            Map<String, Double> interests = new HashMap<>();

            String keyPrefix = "user_realtime_interest:" + userId + ":";
            Set<String> keys = redisTemplate.keys(keyPrefix + "*");

            if (keys != null) {
                for (String key : keys) {
                    String tag = key.substring(keyPrefix.length());
                    Double score = (Double) redisTemplate.opsForValue().get(key);
                    if (score != null) {
                        interests.put(tag, score);
                    }
                }
            }

            return interests;
        }
    }

    /**
     * 用户行为事件
     */
    @lombok.Data
    @lombok.Builder
    @lombok.NoArgsConstructor
    @lombok.AllArgsConstructor
    public static class UserBehaviorEvent {
        private String userId;
        private String itemId;
        private BehaviorType behaviorType;
        private long timestamp;
        private Map<String, Object> metadata;

    }

    /**
     * 行为类型枚举
     */
    public enum BehaviorType {
        LIKE,       // 点赞
        COMMENT,    // 评论
        SHARE,      // 分享
        COLLECT,    // 收藏
        VIEW,       // 浏览
        CLICK       // 点击
    }
}


